#nullable enable

using CsvHelper;
using CsvHelper.Configuration;
using CsvHelper.Configuration.Attributes;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

namespace CsvChartApp
{
    public partial class Form1 : Form
    {
        #region UI Controls and Constants

        // UI Controls
        private Chart chartUtilization = null!;
        private Chart chartRates = null!;
        private Chart chartOpticalPower = null!;
        private TabControl tabControl = null!;
        private Button btnLoadCsv = null!;
        private Button btnResetZoom = null!;
        private Button btnExportChart = null!;
        private TableLayoutPanel tableLayoutPanel = null!;
        private FlowLayoutPanel buttonPanel = null!;
        private ComboBox cboDataSourceFilter = null!;
        private Label lblFilter = null!;
        private Label lblPortStatus = null!;
        private Label lblDisplayMode = null!;

        // Data & Configuration
        private List<CsvDataPoint>? allValidData;

        private readonly string[] expectedHeaders = {
            "IP", "Port", "Description", "Time", "Input Rate (bits/sec)", "Output Rate (bits/sec)",
            "Input Utilization (%)", "Output Utilization (%)", "PortStatus",
            "Current RX Power (dBm)", "RX Power High Thresh (dBm)", "RX Power Low Thresh (dBm)",
            "Current TX Power (dBm)", "TX Power High Thresh (dBm)", "TX Power Low Thresh (dBm)"
        };

        private const long MAX_FILE_SIZE_BYTES = 50 * 1024 * 1024;
        private const int MAX_RECORDS_TO_PROCESS = 2_000_000;

        #endregion

        public Form1()
        {
            InitializeCustomControls();
        }

        #region UI Initialization

        private void InitializeCustomControls()
        {
            this.Text = "带宽与光功率分析工具";
            this.Width = 1200;
            this.Height = 800;
            this.MinimumSize = new Size(800, 600);
            this.StartPosition = FormStartPosition.CenterScreen;

            InitializeLayout();
            InitializeUtilizationChart();
            InitializeRatesChart();
            InitializeOpticalPowerChart();
        }

        private void InitializeLayout()
        {
            tableLayoutPanel = new TableLayoutPanel
            {
                Dock = DockStyle.Fill,
                ColumnCount = 1,
                RowCount = 2,
            };
            tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
            this.Controls.Add(tableLayoutPanel);

            buttonPanel = new FlowLayoutPanel
            {
                Dock = DockStyle.Fill,
                AutoSize = true,
                Padding = new Padding(10, 10, 10, 0),
                WrapContents = false
            };
            tableLayoutPanel.Controls.Add(buttonPanel, 0, 0);

            btnLoadCsv = new Button { Text = "选择 CSV 文件", Width = 150, Height = 30, Margin = new Padding(5) };
            btnLoadCsv.Click += BtnLoadCsv_Click;

            btnResetZoom = new Button { Text = "重置缩放", Width = 120, Height = 30, Enabled = false, Margin = new Padding(5) };
            btnResetZoom.Click += BtnResetZoom_Click;

            btnExportChart = new Button { Text = "导出当前图表", Width = 140, Height = 30, Enabled = false, Margin = new Padding(5) };
            btnExportChart.Click += BtnExportChart_Click;

            lblFilter = new Label { Text = "数据源:", AutoSize = true, Margin = new Padding(15, 5, 0, 5), Padding = new Padding(0, 8, 0, 0) };

            cboDataSourceFilter = new ComboBox { Width = 350, Height = 30, Enabled = false, Margin = new Padding(5), DropDownStyle = ComboBoxStyle.DropDownList };
            cboDataSourceFilter.SelectedIndexChanged += CboDataSourceFilter_SelectedIndexChanged;

            lblPortStatus = new Label
            {
                Text = "状态: -",
                AutoSize = true,
                Margin = new Padding(15, 5, 0, 5),
                Padding = new Padding(0, 8, 0, 0),
                Font = new Font(this.Font, FontStyle.Bold)
            };

            lblDisplayMode = new Label
            {
                Text = "模式: -",
                AutoSize = true,
                Margin = new Padding(20, 5, 0, 5),
                Padding = new Padding(0, 8, 0, 0),
                ForeColor = Color.SteelBlue,
                Font = new Font(this.Font, FontStyle.Italic)
            };

            buttonPanel.Controls.AddRange(new Control[] { btnLoadCsv, btnResetZoom, btnExportChart, lblFilter, cboDataSourceFilter, lblPortStatus, lblDisplayMode });

            tabControl = new TabControl { Dock = DockStyle.Fill, Margin = new Padding(10, 0, 10, 10) };
            tabControl.TabPages.Add(new TabPage("带宽利用率"));
            tabControl.TabPages.Add(new TabPage("流量速率"));
            tabControl.TabPages.Add(new TabPage("光功率"));
            tableLayoutPanel.Controls.Add(tabControl, 0, 1);
        }

        private void InitializeUtilizationChart()
        {
            chartUtilization = new Chart { Dock = DockStyle.Fill };
            var chartArea = new ChartArea("UtilizationArea");
            chartUtilization.ChartAreas.Add(chartArea);
            SetupAxis(chartArea, "利用率 (%)");

            var seriesIn = new Series("输入利用率 (%)") { ChartType = SeriesChartType.Line, BorderWidth = 2, Color = Color.DodgerBlue, XValueType = ChartValueType.DateTime, ToolTip = "时间: #VALX{yyyy-MM-dd HH:mm}\n输入利用率: #VALY{F2}%" };
            var seriesOut = new Series("输出利用率 (%)") { ChartType = SeriesChartType.Line, BorderWidth = 2, Color = Color.OrangeRed, XValueType = ChartValueType.DateTime, ToolTip = "时间: #VALX{yyyy-MM-dd HH:mm}\n输出利用率: #VALY{F2}%" };

            chartUtilization.Series.Add(seriesIn);
            chartUtilization.Series.Add(seriesOut);
            chartUtilization.Legends.Add(new Legend("Legend1") { Docking = Docking.Top });
            tabControl.TabPages[0].Controls.Add(chartUtilization);
        }

        private void InitializeRatesChart()
        {
            chartRates = new Chart { Dock = DockStyle.Fill };
            var chartArea = new ChartArea("RatesArea");
            chartRates.ChartAreas.Add(chartArea);
            SetupAxis(chartArea, "速率 (bits/sec)");

            var seriesIn = new Series("输入速率 (bits/sec)") { ChartType = SeriesChartType.Line, BorderWidth = 2, Color = Color.MediumSeaGreen, XValueType = ChartValueType.DateTime, ToolTip = "时间: #VALX{yyyy-MM-dd HH:mm}\n输入速率: #VALY{N0} bps" };
            var seriesOut = new Series("输出速率 (bits/sec)") { ChartType = SeriesChartType.Line, BorderWidth = 2, Color = Color.MediumPurple, XValueType = ChartValueType.DateTime, ToolTip = "时间: #VALX{yyyy-MM-dd HH:mm}\n输出速率: #VALY{N0} bps" };

            chartRates.Series.Add(seriesIn);
            chartRates.Series.Add(seriesOut);
            chartRates.Legends.Add(new Legend("Legend2") { Docking = Docking.Top });
            tabControl.TabPages[1].Controls.Add(chartRates);
        }

        private void InitializeOpticalPowerChart()
        {
            chartOpticalPower = new Chart { Dock = DockStyle.Fill };
            var chartArea = new ChartArea("OpticalPowerArea");
            chartOpticalPower.ChartAreas.Add(chartArea);
            SetupAxis(chartArea, "功率 (dBm)");

            var seriesRx = new Series("接收光功率 (dBm)") { ChartType = SeriesChartType.Line, BorderWidth = 2, Color = Color.DarkCyan, XValueType = ChartValueType.DateTime, ToolTip = "时间: #VALX{yyyy-MM-dd HH:mm}\n接收功率: #VALY{F2} dBm" };
            var seriesTx = new Series("发送光功率 (dBm)") { ChartType = SeriesChartType.Line, BorderWidth = 2, Color = Color.Goldenrod, XValueType = ChartValueType.DateTime, ToolTip = "时间: #VALX{yyyy-MM-dd HH:mm}\n发送功率: #VALY{F2} dBm" };

            chartOpticalPower.Series.Add(seriesRx);
            chartOpticalPower.Series.Add(seriesTx);
            chartOpticalPower.Legends.Add(new Legend("Legend3") { Docking = Docking.Top });
            tabControl.TabPages[2].Controls.Add(chartOpticalPower);
        }

        #endregion

        #region Event Handlers

        private async void BtnLoadCsv_Click(object? sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog { Filter = "CSV 文件 (*.csv)|*.csv", Title = "选择 CSV 文件" })
            {
                if (openFileDialog.ShowDialog() != DialogResult.OK) return;

                SetUiState(isLoading: true);
                ClearAllChartsAndFilter();
                try
                {
                    string filePath = openFileDialog.FileName;
                    allValidData = await Task.Run(() => ProcessCsvFile(filePath));

                    if (allValidData == null || !allValidData.Any())
                    {
                        MessageBox.Show("文件中未找到有效的、可用于绘图的数据。", "无数据", MessageBoxButtons.OK, MessageBoxIcon.Information);
                        allValidData = null;
                    }
                    else
                    {
                        PopulateFilterComboBox();
                    }
                }
                catch (Exception ex)
                {
                    allValidData = null;
                    MessageBox.Show($"文件处理失败: \n\n{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    SetUiState(isLoading: false, dataLoaded: allValidData != null && allValidData.Any());
                }
            }
        }

        private void CboDataSourceFilter_SelectedIndexChanged(object? sender, EventArgs e)
        {
            if (cboDataSourceFilter.SelectedItem != null)
            {
                FilterAndDrawCharts(cboDataSourceFilter.SelectedItem);
            }
        }

        private void BtnExportChart_Click(object? sender, EventArgs e)
        {
            if (tabControl.SelectedTab == null) return;
            Chart? chartToExport = tabControl.SelectedTab.Controls.OfType<Chart>().FirstOrDefault();

            if (chartToExport == null || cboDataSourceFilter.SelectedItem == null)
            {
                MessageBox.Show("请先加载数据并选择一个数据源。", "操作无效", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            using (SaveFileDialog saveFileDialog = new SaveFileDialog())
            {
                string baseName = SanitizeFileName(cboDataSourceFilter.SelectedItem.ToString() ?? "chart");
                string tabName = SanitizeFileName(tabControl.SelectedTab.Text);
                saveFileDialog.FileName = $"{baseName}_{tabName}_{DateTime.Now:yyyyMMddHHmmss}.png";
                saveFileDialog.Filter = "PNG 图片 (*.png)|*.png|JPEG 图片 (*.jpg)|*.jpg";
                saveFileDialog.Title = "导出当前图表";

                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    try
                    {
                        ImageFormat format = saveFileDialog.FileName.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) ? ImageFormat.Jpeg : ImageFormat.Png;
                        chartToExport.SaveImage(saveFileDialog.FileName, format);
                        MessageBox.Show($"图表已成功导出到:\n\n{saveFileDialog.FileName}", "导出成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show($"导出过程中发生错误:\n\n{ex.Message}", "导出失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void BtnResetZoom_Click(object? sender, EventArgs e)
        {
            foreach (var chart in new[] { chartUtilization, chartRates, chartOpticalPower })
            {
                if (chart.ChartAreas.Count > 0)
                {
                    chart.ChartAreas[0].AxisX.ScaleView.ZoomReset(0);
                    chart.ChartAreas[0].AxisY.ScaleView.ZoomReset(0);
                }
            }
        }

        #endregion

        #region Core Logic

        private void PopulateFilterComboBox()
        {
            if (allValidData == null) return;

            var dataSources = allValidData
                .Select(d => d.DataSourceInfo)
                .Distinct()
                .OrderBy(ds => ds.Ip).ThenBy(ds => ds.Port)
                .ToList();

            cboDataSourceFilter.Items.Clear();
            cboDataSourceFilter.Items.AddRange(dataSources.Cast<object>().ToArray());

            cboDataSourceFilter.Enabled = dataSources.Any();
            if (dataSources.Any())
            {
                cboDataSourceFilter.SelectedIndex = 0;
            }
        }

        private void FilterAndDrawCharts(object filterObject)
        {
            if (allValidData == null || !(filterObject is DataSourceIdentifier selectedDataSource))
            {
                ClearAllCharts();
                return;
            }

            var rawDataToPlot = allValidData.Where(d => d.DataSourceInfo.Equals(selectedDataSource)).ToList();
            ClearAllCharts();

            if (!rawDataToPlot.Any())
            {
                UpdatePortStatusLabel(null);
                lblDisplayMode.Text = "模式: -";
                MessageBox.Show("选定的数据源没有有效的数据点。", "无数据", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return;
            }

            List<CsvDataPoint> dataForChart;
            var duration = rawDataToPlot.Last().Timestamp - rawDataToPlot.First().Timestamp;

            if (duration.TotalDays > 3)
            {
                dataForChart = AggregateData(rawDataToPlot, TimeSpan.FromHours(1));
                tabControl.TabPages[0].Text = "带宽利用率 (每小时平均值)";
                tabControl.TabPages[1].Text = "流量速率 (每小时峰值)";
                tabControl.TabPages[2].Text = "光功率 (每小时平均值)";
                lblDisplayMode.Text = "模式: 月报聚合 (周期 > 3天)";
                lblDisplayMode.ForeColor = Color.Crimson;
            }
            else
            {
                dataForChart = rawDataToPlot;
                tabControl.TabPages[0].Text = "带宽利用率";
                tabControl.TabPages[1].Text = "流量速率";
                tabControl.TabPages[2].Text = "光功率";
                lblDisplayMode.Text = "模式: 详细数据 (周期 ≤ 3天)";
                lblDisplayMode.ForeColor = Color.SteelBlue;
            }

            if (!dataForChart.Any())
            {
                UpdatePortStatusLabel(null);
                return;
            }

            chartUtilization.SuspendLayout();
            chartRates.SuspendLayout();
            chartOpticalPower.SuspendLayout();

            var inputUtilSeries = chartUtilization.Series[0];
            var outputUtilSeries = chartUtilization.Series[1];
            var inputRateSeries = chartRates.Series[0];
            var outputRateSeries = chartRates.Series[1];
            var rxPowerSeries = chartOpticalPower.Series[0];
            var txPowerSeries = chartOpticalPower.Series[1];

            foreach (var dp in dataForChart)
            {
                inputUtilSeries.Points.AddXY(dp.Timestamp, dp.InputUtilization);
                outputUtilSeries.Points.AddXY(dp.Timestamp, dp.OutputUtilization);
                inputRateSeries.Points.AddXY(dp.Timestamp, dp.InputRate);
                outputRateSeries.Points.AddXY(dp.Timestamp, dp.OutputRate);

                if (dp.RxPower.HasValue)
                {
                    rxPowerSeries.Points.AddXY(dp.Timestamp, dp.RxPower.Value);
                }
                if (dp.TxPower.HasValue)
                {
                    txPowerSeries.Points.AddXY(dp.Timestamp, dp.TxPower.Value);
                }
            }

            AddStatusAnnotations(chartUtilization, rawDataToPlot);
            AddStatusAnnotations(chartRates, rawDataToPlot);
            // 关键修复：为光功率图表也添加状态注释
            AddStatusAnnotations(chartOpticalPower, rawDataToPlot);

            ConfigureXAxis(chartUtilization.ChartAreas[0], dataForChart);
            ConfigureXAxis(chartRates.ChartAreas[0], dataForChart);
            ConfigureXAxis(chartOpticalPower.ChartAreas[0], dataForChart);

            chartUtilization.ResumeLayout();
            chartRates.ResumeLayout();
            chartOpticalPower.ResumeLayout();

            UpdatePortStatusLabel(rawDataToPlot.LastOrDefault());
        }

        #endregion

        #region Helper Methods & Data Models

        private List<CsvDataPoint> AggregateData(List<CsvDataPoint> points, TimeSpan interval)
        {
            if (!points.Any()) return new List<CsvDataPoint>();

            return points
                .GroupBy(p => p.Timestamp.Ticks / interval.Ticks)
                .Select(group =>
                {
                    var firstPoint = group.First();
                    var intervalStartTimestamp = new DateTime(group.Key * interval.Ticks);

                    double maxInputRate = group.Max(p => p.InputRate);
                    double maxOutputRate = group.Max(p => p.OutputRate);
                    double avgInputUtil = group.Average(p => p.InputUtilization);
                    double avgOutputUtil = group.Average(p => p.OutputUtilization);
                    string lastStatus = group.Last().PortStatus;

                    var validRxValues = group.Select(p => p.RxPower).Where(v => v.HasValue).ToList();
                    double? avgRxPower = validRxValues.Any() ? validRxValues.Average() : null;

                    var validTxValues = group.Select(p => p.TxPower).Where(v => v.HasValue).ToList();
                    double? avgTxPower = validTxValues.Any() ? validTxValues.Average() : null;

                    return new CsvDataPoint(intervalStartTimestamp, firstPoint.DataSourceInfo,
                                            maxInputRate, maxOutputRate, avgInputUtil, avgOutputUtil, lastStatus,
                                            avgRxPower, avgTxPower);
                })
                .OrderBy(p => p.Timestamp)
                .ToList();
        }

        private void AddStatusAnnotations(Chart chart, List<CsvDataPoint> data)
        {
            chart.Annotations.Clear();
            if (data.Count < 2) return;

            string previousStatus = data.First().PortStatus;

            for (int i = 1; i < data.Count; i++)
            {
                var currentPoint = data[i];
                string currentStatus = currentPoint.PortStatus;
                if (currentStatus != previousStatus)
                {
                    Color lineColor = currentStatus.Contains("up") ? Color.FromArgb(128, Color.ForestGreen) : Color.FromArgb(128, Color.Red);
                    var annotation = new VerticalLineAnnotation
                    {
                        AxisX = chart.ChartAreas[0].AxisX,
                        AxisY = chart.ChartAreas[0].AxisY,
                        X = currentPoint.Timestamp.ToOADate(),
                        IsInfinitive = true,
                        LineColor = lineColor,
                        LineWidth = 2,
                        LineDashStyle = ChartDashStyle.Dash,
                        ToolTip = $"时间: {currentPoint.Timestamp:HH:mm:ss}\n状态从 '{previousStatus.ToUpper()}' 变为 '{currentStatus.ToUpper()}'",
                        ClipToChartArea = chart.ChartAreas[0].Name
                    };
                    chart.Annotations.Add(annotation);
                    previousStatus = currentStatus;
                }
            }
        }

        private List<CsvDataPoint> ProcessCsvFile(string filePath)
        {
            var fileInfo = new FileInfo(filePath);
            if (fileInfo.Length > MAX_FILE_SIZE_BYTES)
            {
                throw new InvalidDataException($"文件大小超出限制 ({MAX_FILE_SIZE_BYTES / 1024 / 1024} MB)。");
            }

            var config = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = true, BadDataFound = null, MissingFieldFound = null, HeaderValidated = null, TrimOptions = TrimOptions.Trim };
            using (var reader = new StreamReader(filePath, Encoding.UTF8))
            using (var csv = new CsvReader(reader, config))
            {
                ValidateCsvHeader(csv);
                return csv.GetRecords<CsvRecord>()
                          .Take(MAX_RECORDS_TO_PROCESS)
                          .Select(r => new CsvDataPoint(r))
                          .Where(dp => dp.IsValid)
                          .OrderBy(dp => dp.Timestamp)
                          .ToList();
            }
        }

        private void UpdatePortStatusLabel(CsvDataPoint? latestPoint)
        {
            if (latestPoint == null)
            {
                lblPortStatus.Text = "状态: -";
                lblPortStatus.ForeColor = SystemColors.ControlText;
                return;
            }
            string status = latestPoint.PortStatus;
            lblPortStatus.Text = $"状态: {status.ToUpper()}";
            lblPortStatus.ForeColor = status.Contains("up") ? Color.ForestGreen : (status.Contains("down") || status.Contains("disable") ? Color.Red : Color.DarkOrange);
        }

        private void SetUiState(bool isLoading, bool dataLoaded = false)
        {
            this.Cursor = isLoading ? Cursors.WaitCursor : Cursors.Default;
            btnLoadCsv.Enabled = !isLoading;
            btnResetZoom.Enabled = !isLoading && dataLoaded;
            btnExportChart.Enabled = !isLoading && dataLoaded;
            cboDataSourceFilter.Enabled = !isLoading && dataLoaded;
        }

        private void ClearAllCharts()
        {
            foreach (var chart in new[] { chartUtilization, chartRates, chartOpticalPower })
            {
                chart.Annotations.Clear();
                foreach (var series in chart.Series)
                {
                    series.Points.Clear();
                }
            }
        }

        private void ClearAllChartsAndFilter()
        {
            ClearAllCharts();
            cboDataSourceFilter.Items.Clear();
            cboDataSourceFilter.Enabled = false;
            UpdatePortStatusLabel(null);
            lblDisplayMode.Text = "模式: -";
        }

        private void ValidateCsvHeader(CsvReader csv)
        {
            csv.Read();
            csv.ReadHeader();
            string[]? headerRecord = csv.Context.Reader.HeaderRecord;
            if (headerRecord == null)
            {
                throw new InvalidDataException("CSV文件没有有效的表头。");
            }
            string[] actualHeaders = headerRecord.Select(h => h.Trim()).ToArray();
            if (actualHeaders.Length != expectedHeaders.Length)
            {
                throw new InvalidDataException($"列数不匹配。预期: {expectedHeaders.Length}, 实际: {actualHeaders.Length}。");
            }
            for (int i = 0; i < expectedHeaders.Length; i++)
            {
                if (!string.Equals(expectedHeaders[i], actualHeaders[i], StringComparison.OrdinalIgnoreCase))
                {
                    throw new InvalidDataException($"列名或顺序不正确。第{i + 1}列, 预期: '{expectedHeaders[i]}', 实际: '{actualHeaders[i]}'");
                }
            }
        }

        private void SetupAxis(ChartArea chartArea, string yAxisTitle)
        {
            chartArea.AxisY.IsStartedFromZero = false;
            Axis xAxis = chartArea.AxisX;
            xAxis.Title = "时间";
            xAxis.MajorGrid.LineColor = Color.LightGray;
            xAxis.LabelStyle.Angle = -45;
            xAxis.LabelStyle.IsEndLabelVisible = true;
            Axis yAxis = chartArea.AxisY;
            yAxis.Title = yAxisTitle;
            yAxis.MajorGrid.LineColor = Color.LightGray;
            yAxis.LabelStyle.Format = "N2";
            chartArea.CursorX.IsUserEnabled = true;
            chartArea.CursorX.IsUserSelectionEnabled = true;
            chartArea.AxisX.ScaleView.Zoomable = true;
            chartArea.AxisX.ScrollBar.IsPositionedInside = false;
            chartArea.CursorY.IsUserEnabled = true;
            chartArea.CursorY.IsUserSelectionEnabled = true;
            chartArea.AxisY.ScaleView.Zoomable = true;
            chartArea.AxisY.ScrollBar.IsPositionedInside = false;
        }

        private void ConfigureXAxis(ChartArea chartArea, List<CsvDataPoint> dataPoints)
        {
            var xAxis = chartArea.AxisX;
            xAxis.Minimum = double.NaN;
            xAxis.Maximum = double.NaN;
            xAxis.Interval = 0;
            xAxis.IntervalType = DateTimeIntervalType.Auto;

            if (dataPoints.Count < 2)
            {
                xAxis.LabelStyle.Format = "HH:mm";
                return;
            }

            var minTime = dataPoints.First().Timestamp;
            var maxTime = dataPoints.Last().Timestamp;
            var duration = maxTime - minTime;

            if (duration.TotalMinutes <= 10)
            {
                xAxis.LabelStyle.Format = "HH:mm:ss";
                xAxis.IntervalType = DateTimeIntervalType.Seconds;
                xAxis.Interval = Math.Max(1, Math.Ceiling(duration.TotalSeconds / 5.0));
            }
            else if (duration.TotalHours <= 3)
            {
                xAxis.LabelStyle.Format = "HH:mm";
                xAxis.IntervalType = DateTimeIntervalType.Minutes;
                xAxis.Interval = Math.Max(1, Math.Ceiling(duration.TotalMinutes / 6.0));
            }
            else if (duration.TotalDays <= 7)
            {
                xAxis.LabelStyle.Format = "MM-dd HH:mm";
                xAxis.IntervalType = DateTimeIntervalType.Hours;
                xAxis.Interval = Math.Max(1, Math.Ceiling(duration.TotalHours / 8.0));
            }
            else
            {
                xAxis.LabelStyle.Format = "yyyy-MM-dd";
                xAxis.IntervalType = DateTimeIntervalType.Days;
                xAxis.Interval = Math.Max(1, Math.Ceiling(duration.TotalDays / 10.0));
            }
        }

        private string SanitizeFileName(string name) => string.Join("_", name.Split(Path.GetInvalidFileNameChars()));

        public class DataSourceIdentifier : IEquatable<DataSourceIdentifier>
        {
            public string Ip { get; }
            public string Port { get; }
            public string Description { get; }
            public DataSourceIdentifier(string ip, string port, string description) { Ip = ip ?? ""; Port = port ?? ""; Description = description ?? ""; }
            public override string ToString() => $"{Ip} ({Port}) - {Description}";
            public bool Equals(DataSourceIdentifier? other) => other != null && Ip == other.Ip && Port == other.Port && Description == other.Description;
            public override bool Equals(object? obj) => Equals(obj as DataSourceIdentifier);
            public override int GetHashCode() => (Ip, Port, Description).GetHashCode();
        }

        public class CsvDataPoint
        {
            public DateTime Timestamp { get; }
            public DataSourceIdentifier DataSourceInfo { get; }
            public double InputRate { get; }
            public double OutputRate { get; }
            public double InputUtilization { get; }
            public double OutputUtilization { get; }
            public string PortStatus { get; }
            public double? RxPower { get; }
            public double? TxPower { get; }
            public bool IsValid { get; }

            public CsvDataPoint(CsvRecord rawRecord)
            {
                DataSourceInfo = new DataSourceIdentifier(rawRecord.IP, rawRecord.Port, rawRecord.Description);
                bool timeParsed = DateTime.TryParse(rawRecord.Time, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var ts);
                Timestamp = ts;
                InputRate = rawRecord.InputRate;
                OutputRate = rawRecord.OutputRate;
                InputUtilization = rawRecord.InputUtilization;
                OutputUtilization = rawRecord.OutputUtilization;
                PortStatus = rawRecord.PortStatus?.Trim().ToLowerInvariant() ?? "unknown";
                RxPower = rawRecord.RxPower;
                TxPower = rawRecord.TxPower;

                IsValid = timeParsed &&
                          !string.IsNullOrWhiteSpace(PortStatus) && PortStatus != "unknown" &&
                          !double.IsNaN(InputUtilization) && !double.IsInfinity(InputUtilization) &&
                          !double.IsNaN(OutputUtilization) && !double.IsInfinity(OutputUtilization) &&
                          !double.IsNaN(InputRate) && !double.IsInfinity(InputRate) &&
                          !double.IsNaN(OutputRate) && !double.IsInfinity(OutputRate);
            }

            public CsvDataPoint(DateTime timestamp, DataSourceIdentifier dataSource, double inputRate, double outputRate, double inputUtil, double outputUtil, string portStatus, double? rxPower, double? txPower)
            {
                Timestamp = timestamp;
                DataSourceInfo = dataSource;
                InputRate = inputRate;
                OutputRate = outputRate;
                InputUtilization = inputUtil;
                OutputUtilization = outputUtil;
                PortStatus = portStatus;
                RxPower = rxPower;
                TxPower = txPower;
                IsValid = true;
            }
        }

        public class CsvRecord
        {
            [Index(0)] public string IP { get; set; } = string.Empty;
            [Index(1)] public string Port { get; set; } = string.Empty;
            [Index(2)] public string Description { get; set; } = string.Empty;
            [Index(3)] public string Time { get; set; } = string.Empty;
            [Index(4)] [Name("Input Rate (bits/sec)")] public double InputRate { get; set; }
            [Index(5)] [Name("Output Rate (bits/sec)")] public double OutputRate { get; set; }
            [Index(6)] [Name("Input Utilization (%)")] public double InputUtilization { get; set; }
            [Index(7)] [Name("Output Utilization (%)")] public double OutputUtilization { get; set; }
            [Index(8)] [Name("PortStatus")] public string PortStatus { get; set; } = string.Empty;
            [Index(9)] [Name("Current RX Power (dBm)")] public double? RxPower { get; set; }
            [Index(10)] [Name("RX Power High Thresh (dBm)")] public double? RxHighThresh { get; set; }
            [Index(11)] [Name("RX Power Low Thresh (dBm)")] public double? RxLowThresh { get; set; }
            [Index(12)] [Name("Current TX Power (dBm)")] public double? TxPower { get; set; }
            [Index(13)] [Name("TX Power High Thresh (dBm)")] public double? TxHighThresh { get; set; }
            [Index(14)] [Name("TX Power Low Thresh (dBm)")] public double? TxLowThresh { get; set; }
        }

        #endregion
    }
}